package org.onion.pattern.event.consumer.log;

import com.github.f4b6a3.uuid.UuidCreator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.onion.pattern.event.consumer.EventConsumeHandler;
import org.onion.pattern.event.consumer.EventConsumeInterceptor;
import org.onion.pattern.event.consumer.ExternalEventInfo;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * 消费日志
 */
@Slf4j
public abstract class EventConsumeLogInterceptor implements EventConsumeInterceptor {


    private final ThreadLocal<EventConsumeLog> threadLocal = new InheritableThreadLocal<>();

    protected abstract Optional<EventConsumeLog> toEventConsumeLog(ExternalEventInfo eventInfo, Object metadata, EventConsumeHandler handler);

    @Override
    public boolean preHandle(ExternalEventInfo eventInfo, Object metadata, EventConsumeHandler handler) {
        try {
            threadLocal.set(null);
            EventConsumeLog eventConsumeLog = null;
            try {
                eventConsumeLog = this.toEventConsumeLog(eventInfo, metadata, handler).orElse(null);
            } catch (Exception e) {
                log.error("消费日志", e);
            }
            if (eventConsumeLog == null) {
                return true;
            }

            if (StringUtils.isBlank(eventConsumeLog.getId())) {
                eventConsumeLog.setId(StringUtils.replace(UuidCreator.getTimeOrderedEpoch().toString(), "-", ""));
            }

            if (StringUtils.isBlank(eventConsumeLog.getEventKey())) {
                eventConsumeLog.setEventKey(eventInfo.getEventKey());
            }

            if (eventConsumeLog.getReceiveTime() == null) {
                eventConsumeLog.setReceiveTime(LocalDateTime.now());
            }

            if (eventConsumeLog.getEventTime() == null) {
                eventConsumeLog.setEventTime(eventInfo.getTs());
            }

            threadLocal.set(eventConsumeLog);
        } catch (Throwable e) {
            log.warn("",e);
            threadLocal.set(null);
        }

        return true;
    }


    @Override
    public void afterCompletion(ExternalEventInfo eventInfo, Object metadata, EventConsumeHandler handler, Exception ex) {
        try {
            EventConsumeLog consumeLog = threadLocal.get();
            threadLocal.set(null);
            if (consumeLog == null) {
                return;
            }

            consumeLog.setConsumedTime(LocalDateTime.now());

            long publishMillis = Duration.between(consumeLog.getEventTime(), consumeLog.getPublishedTime()).toMillis();
            long receiveMillis = Duration.between(consumeLog.getEventTime(), consumeLog.getReceiveTime()).toMillis();
            long totalMillis = Duration.between(consumeLog.getEventTime(), consumeLog.getConsumedTime()).toMillis();
            if (publishMillis > 70 || receiveMillis > 150 || totalMillis > 7000) {
                log.warn("【事件消费日志】 耗时消费 publish {} ms | receive {} ms  |  total {} ms  eventKey {}  logId {}", publishMillis, receiveMillis, totalMillis,
                        consumeLog.getEventKey(), consumeLog.getId());
            } else {
                log.info("【事件消费日志】 耗时消费 publish {} ms | receive {} ms  |  total {} ms  eventKey {}  logId {}", publishMillis, receiveMillis, totalMillis,
                        consumeLog.getEventKey(), consumeLog.getId());
            }

            if (ex == null) {
                consumeLog.setConsumeStatus(EventConsumeStatusEnum.SUCCESS);
            } else {
                consumeLog.setConsumeStatus(EventConsumeStatusEnum.FAIL);
                consumeLog.setExceptionStack(StringUtils.abbreviate(ExceptionUtils.getStackTrace(ex), 1000));
            }

            try {
                if (this.getLogStorage() == null) {
                    log.warn("消费日志 logStorage 为空");
                    return;
                }
                this.getLogStorage().save(consumeLog);
            } catch (Exception e) {
                log.error("消费日志", e);
            }
        } catch (Throwable e) {
            log.warn("",e);
        }finally {
            threadLocal.set(null);
        }

    }

    @Override
    public int order() {
        return Integer.MIN_VALUE; //优先级最高
    }


    protected abstract EventConsumeLogStorage getLogStorage();

}
